﻿/*
Copyright (C) 2010 INOUE Hiroyuki

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/

using System;
using System.Collections;
using System.Collections.Generic;
using IO = System.IO;
using System.Linq;
using OP = CommandLine.OptParse;

namespace CodePlex.Dombly.DiskUsage.ConsoleApplication
{
    using Extensions;
    class Program
    {
        #region fields
        /// <summary>List of file size infomation of directories.</summary>
        static List<DirectorySizeInfo> TopDirs;
        /// <summary>Set of files sorted by file-size descendant.</summary>
        static FixedSizeSortedSet<FileSizeInfo> TopFiles;
        #endregion

        #region Main
        static void Main(string[] args)
        {
            try
            {
                // parse options in the arguments
                OP.Parser parser = OP.ParserFactory.BuildParser(Options.Current);
                string[] targets = parser.Parse(args);

                // start snapshot
                if (Options.Current.SnapShot)
                {
                    Console.WriteLine();
                    Console.WriteLine("[*** START: {0} ***]\n", DateTime.Now);
                }

                #region add event handlers as necessary
                if (Options.Current.TraversalReportDepth >= 0)
                {
                    BusinessLogic.Directory.DirectoryScanCompleted += ScanCompleted_WriteSizeInfo;
                    if (Options.Current.TraversalReportWithFile)
                        BusinessLogic.File.FileScanCompleted += ScanCompleted_WriteSizeInfo;
                }
                if (Options.Current.TopDirectories > 0)
                {
                    TopDirs = new List<DirectorySizeInfo>();
                    BusinessLogic.Directory.DirectoryScanCompleted +=
                        ScanCompleted_RegisterTopDirectories;
                }
                if (Options.Current.TopFiles > 0)
                {
                    TopFiles = new FixedSizeSortedSet<FileSizeInfo>(Options.Current.TopFiles);
                    BusinessLogic.Directory.DirectoryScanCompleted +=
                        ScanCompleted_RegisterTopFiles;
                }

                BusinessLogic.Directory.DirectoryScanCompleted +=
                    (sender, e) => ((BusinessLogic.Directory)sender).Clear();
                #endregion

                // loop for targets
                foreach (var target in GetTargets(targets))
                {
                    Console.WriteLine();

                    if (TopDirs != null) TopDirs.Clear();
                    if (TopFiles != null) TopFiles.Clear();

                    target.Aggregate(Options.Current.TraversalReportDepth);

                    if (TopDirs != null && TopDirs.Any())
                    {
                        TopDirs.Sort((x, y) =>
                            {
                                int comparison = x.Depth - y.Depth;
                                if (comparison == 0) comparison = y.FileSize.CompareTo(x.FileSize);
                                if (comparison == 0) comparison = x.FullName.CompareTo(y.FullName);
                                return comparison;
                            });
                        foreach (var dir in TopDirs)
                        {
                            if (!dir.Directories.Any()) continue;
                            Console.WriteLine();
                            Console.WriteLine("*** Top {0} dirs in {1} ***",
                                dir.Directories.Count,
                                dir.FullName);
                            foreach (var subdir in dir.Directories) subdir.WriteInfo();
                        }
                    }

                    if (TopFiles != null && TopFiles.Any())
                    {
                        Console.WriteLine();
                        Console.WriteLine("*** Top {0} files in {1} ***",
                            TopFiles.Count,
                            target.FullName);
                        foreach (var file in TopFiles) file.WriteInfo();
                    }
                }

                // end snapshot
                if (Options.Current.SnapShot)
                {
                    Console.WriteLine();
                    Console.WriteLine();
                    Console.WriteLine("[***   END: {0} ***]", DateTime.Now);
                }
            }
            catch (Exception exc)
            {
                Console.Error.WriteLine(exc);
            }
        }
        #endregion

        #region methods

        #region GetTargets
        /// <summary>
        /// Evaluate wild cards and generate target Directory enumeration
        /// </summary>
        /// <param name="targetArgs">
        /// Target definitions, which can include WinShell wild-card characters.
        /// </param>
        /// <returns>Enumerable of Directory objects.</returns>
        private static IEnumerable<BusinessLogic.Directory> GetTargets(string[] targetArgs)
        {
            foreach (string target in targetArgs)
            {
                if (new[] { '*', '?', '[', ']' }.Any(c => target.Contains(c)))
                {
                    foreach (var dir in IO.Directory.GetDirectories(".", target)
                                                    .Select(_ => GenerateDirectory(_))
                                                    .OfType<BusinessLogic.Directory>())
                        yield return dir;
                }
                else
                {
                    var dir = GenerateDirectory(target);
                    if (dir != null) yield return dir;
                }
            }
        }
        #endregion

        #region GenerateDirectory
        /// <summary>
        /// Generates Directory object, trapping exception.
        /// </summary>
        /// <param name="name">Name of Directory to generate.</param>
        /// <returns>Directory object.</returns>
        private static BusinessLogic.Directory GenerateDirectory(string name)
        {
            try
            {
                return new BusinessLogic.Directory(name);
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e.Message);
                return null;
            }
        }
        #endregion

        #endregion

        #region event handlelrs

        #region ScanCompleted_WriteSizeInfo
        /// <summary>
        /// Handles ScanCompleted event for Directory/File instance and 
        /// report its file size information.
        /// </summary>
        private static void ScanCompleted_WriteSizeInfo(object sender, EventArgs e)
        {
            var i = (BusinessLogic.IParent)sender;
            if (i.Parent != null &&
                (Options.Current.TraversalReportDepth == 0 ||
                 Options.Current.TraversalReportDepth <= i.Parent.Depth))
            {
                return;
            }
            i.WriteInfo();
        }
        #endregion

        #region ScanCompleted_RegisterTopDirectories
        /// <summary>
        /// Handles ScanCompleted event for Directory instance and register
        /// the instance into top directories list.
        /// </summary>
        private static void ScanCompleted_RegisterTopDirectories(object sender, EventArgs e)
        {
            var d = (BusinessLogic.Directory)sender;
            if (Options.Current.TopDirectoriesDepth < d.Depth) return;
            lock (((ICollection)TopDirs).SyncRoot)
            {
                TopDirs.Add(new DirectorySizeInfo(d, Options.Current.TopDirectories));
            }
        }
        #endregion

        #region ScanCompleted_RegisterTopFiles
        /// <summary>
        /// Handles ScanCompleted event for Directory instance and register
        /// its files into top files list.
        /// </summary>
        private static void ScanCompleted_RegisterTopFiles(object sender, EventArgs e)
        {
            ((BusinessLogic.Directory)sender)
            .Files
            .OrderByDescending(_ => _.FileSize)
            .All(f =>
               {
                   lock (((ICollection)TopFiles).SyncRoot)
                   {
                       // break if addition failed, which means
                       // current file is smaller than any top file
                       return TopFiles.Add(new FileSizeInfo(f));
                   }
               });
        }
        #endregion

        #endregion
    }
}
